/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package generadorVHDL;

import compiladorEntidad.*;
import java.io.*;
import java.util.Date;

/**
 *
 * @author  Ruben Tarancón
 *          Victor Alaminos
 *          Felipe Serrano
 */
public class GeneraVhdl_V5 {
    /**
     * Entidad que se quiere conectar al módulo de entrada/salida
     */
    private Entidad entidad;

    /**
     * Nombre de la entidad superior. Va a ser siempre Circuito_FPGA
     */
    private String nomEntidadGeneral;

    /**
     * Escritor de fichero
     */
    private BufferedWriter bw;

    /**
     * Ruta del fichero en la que se va a escribir
     */
    private String fichero;
    
    /**
     * Número máximo de entradas/salidas
     */
    private int numMaxBits;

    /**
     * Errores surgidos durante la generación del fichero
     */
    private Errores errores;

    /**
     * Constructor de la clase
     * @param fichero Sobre el que vamos a generar el nuevo vhdl.
     * @param entidad Entidad con la que estamos trabajando y a partir de la cual se genera el vhdl.
     * @param tipoAdaptador será el número de entradas y salidas al que se adaptará el circuito
     * @param errores Objeto de la clase Errores para ir almacenando los errores encontrados.
     * @throws java.io.IOException
     */
    public GeneraVhdl_V5(String fichero, Entidad entidad, int tipoAdaptador, Errores errores) throws IOException {
        this.fichero = fichero;
        this.entidad = entidad;
        this.errores = errores;
        this.numMaxBits = tipoAdaptador-1;
    }

    /**
     * Abre el fichero para escribir el nuevo archivo vhdl.
     * @return Cierto si no ha habido ningún error, falso en caso contrario.
     */
    public boolean abrir() {
        boolean correcto = fichero != null;
        try {
            bw = new BufferedWriter(new FileWriter(fichero));
        } catch (IOException e) {
            errores.error("Error al abrir el fichero " + fichero);
            correcto = false;
        }
        return correcto;
    }

    /**
     * Cierra el fichero VHDL que estamos construyendo
     * @return Cierto si no ha habido ningún error, falso en caso contrario.
     */
    public boolean cerrar() {
        boolean correcto = bw != null;
        try {
            bw.close();
        } catch (IOException e) {
            errores.error("Error al cerrar el fichero " + fichero);
            correcto = false;
        }
        return correcto;
    }

    /**
     * Escribe una cadena en el fichero junto con un salto de linea
     * @param linea Cadena a escribir
     */
    private void escribirLinea(String linea) {
        try {
            bw.write(linea);
            bw.newLine();
        } catch (IOException ex) {
            errores.error("Error al escribir en fichero");
        }
    }

    /**
     * Método para saber si hay reloj
     * @return
     */
    private boolean hayReloj(){
        return this.entidad.getPosicionClk() >= 0;
    }


    /**
     * Escribe comentarios en la cabecera del fichero
     */
    private void comentariosCabecera(){
        escribirLinea("--------------------------------------------------------------------------------");
        escribirLinea("--Descripción:");
        escribirLinea("--\tEste fichero ha sido generado automáticamente por la aplicación Nessy2.0");
        escribirLinea("--\tSe trata de un fichero que se llamara user_logic y que estara situado en la carpeta pcores/adaptador_circuito");
        escribirLinea("--");
        escribirLinea("--");
        escribirLinea("--Especificaciones:");
        escribirLinea("--\tCircuito a ejecutar:");
        escribirLinea("--\t\tNum. Entradas: "+ entidad.getBitsEntrada());
        escribirLinea("--\t\tNum. Salidas: " + entidad.getBitsSalida());
        escribirLinea("--Autores:");
        escribirLinea("--\tRubén Tarancón Garijo");
        escribirLinea("--\tFelipe Serrano Santos");
        escribirLinea("--\tFacultad de Informatica. Universidad Complutense de Madrid");
        Date date = new Date();
        escribirLinea("--Fecha: ");
        escribirLinea("--\t"+date.toString());
        escribirLinea("--------------------------------------------------------------------------------");
        escribirLinea("");
    }
    /**
     * Escribe la inclusión de librerías en el fichero
     */
    private void librerias() {
        escribirLinea("library ieee;");
        escribirLinea("use ieee.std_logic_1164.all;");
        escribirLinea("");
        escribirLinea("");
    }

    /**
     * Descripción de la entidad en la que se integrará nuestro contador y que tendrá las señales para comunicarse con el bus
     */
    private void entidadGeneral() {
        escribirLinea("");
        escribirLinea("entity circuito is");
        escribirLinea("port(");
        escribirLinea("\tdato_listo : in std_logic;");
        escribirLinea("\tentrada_circuito : in std_logic_vector(0 to "+numMaxBits+");");
        escribirLinea("\tsalida_circuito :out std_logic_vector(0 to "+numMaxBits+")");
        escribirLinea(");");
        escribirLinea("end circuito;");
        escribirLinea("");
    }

    /**
     * Inicio de la arquitectura
     */
    private void inicioArquitectura() {
        escribirLinea("");
        escribirLinea("architecture Behavioral of circuito is");
        escribirLinea("");
        escribirLinea("\tattribute use_dsp48: string;");
        escribirLinea("");
        escribirLinea("");
    }

    /**
     * Señales del circuito que queremos adaptar
     */
    private void senalesCircuito() {
        escribirLinea("\t----------------- SEÑALES DEL CIRCUITO -----------------------");
        /*Para cada salida "sal_i" del circuito: senal_sal_i */
        for(int i=0;i<entidad.getNumSalidas();i++){
            Salida s = entidad.getSalida(i);
            if(s.getNumBits()==1){
                escribirLinea("\tsignal SENAL_"+s.getNombre()+"          : std_logic;");
            }else{
                escribirLinea("\tsignal SENAL_"+s.getNombre()+"          : std_logic_vector(0 to "+(s.getNumBits()-1)+");");
            }
        }
        escribirLinea("\t--------------------------------------------------------------");
        escribirLinea("");
    }

    /**
     * Componente del circuito que queremos adaptar
     */
    private void componenteCircuito() {
        escribirLinea("");
        escribirLinea("\t--------------------- COMPONENTE -----------------------------");

        escribirLinea("\tCOMPONENT "+entidad.getNombre()+" PORT (");
        for(int i=0;i<entidad.getNumEntradas();i++){
            Entrada e = entidad.getEntrada(i);
            if(e.getNumBits()==1){
                escribirLinea("\t\tsignal "+e.getNombre()+" : in std_logic;");
            }else{
                escribirLinea("\t\tsignal "+e.getNombre()+" : in std_logic_vector(0 to "+(e.getNumBits()-1)+");");
            }
        }
        for(int i=0;i<entidad.getNumSalidas();i++){
            Salida s = entidad.getSalida(i);
            if(entidad.getNumSalidas()-1==i){
                //EN EL CASO DE QUE SEA LA ÚLTIMA SALIDA NO SE PONE ";" AL FINAL
                if(s.getNumBits()==1){
                    escribirLinea("\t\tsignal "+s.getNombre()+" : out std_logic");
                }else{
                    escribirLinea("\t\tsignal "+s.getNombre()+" : out std_logic_vector(0 to "+(s.getNumBits()-1)+")");
                }
            }else{
                if(s.getNumBits()==1){
                    escribirLinea("\t\tsignal "+s.getNombre()+" : out std_logic;");
                }else{
                    escribirLinea("\t\tsignal "+s.getNombre()+" : out std_logic_vector(0 to "+(s.getNumBits()-1)+");");
                }
            }
        }
        escribirLinea("\t\t);");
        escribirLinea("\tEND COMPONENT;");
        escribirLinea("\t--------------------------------------------------------------");
        escribirLinea("");
        escribirLinea("\tattribute use_dsp48 of " + entidad.getNombre()+" : component is \"no\";");
        escribirLinea("");
    }

    /**
     * begin de la arquitectura
     */
    private void beginArquitectura() {
        escribirLinea("");
        escribirLinea("begin");
        escribirLinea("");
    }
    /**
     * Asignacion de las señales del circuito
     */
    private void asignacionSenalesCircuito() {
        int bit_usados=0;
        int tam_salida;

        escribirLinea("");
        escribirLinea("\t---------------- ASIGNACIÓN DE LAS SALIDAS --------------------");
        for(int i=0;i<entidad.getNumSalidas();i++){
            Salida s = entidad.getSalida(i);
            tam_salida = s.getNumBits();
            if(tam_salida == 1){
                /* CASO STD_LOGIC */
                escribirLinea("\tsalida_circuito("+bit_usados+") <= SENAL_"+s.getNombre()+";");
                bit_usados++;
            }else{
                /* CASO_ STD_LOGIC_VECTOR */
                escribirLinea("\tsalida_circuito("+bit_usados+" to "+(bit_usados+tam_salida-1)+") <= SENAL_"+s.getNombre()+"(0 to "+(tam_salida-1)+");");
                bit_usados = bit_usados + tam_salida;
            }
        }
        escribirLinea("\tsalida_circuito("+bit_usados+" to "+numMaxBits+") <= (OTHERS => '0');");
        escribirLinea("\t---------------------------------------------------------------");
        escribirLinea("");
    }

    /**
     * port map del componente Circuito con las señales del circuito
     */
    private void asignacionComponenteCircuito() {
        int bit_usados=0;
        int tam_entrada;
        escribirLinea("");
        escribirLinea("\t------------------------ MAPEO --------------------------------");
        escribirLinea("\tinstancia : "+entidad.getNombre()+" PORT MAP(");
        for(int i=0;i<entidad.getNumEntradas();i++){
            Entrada e = entidad.getEntrada(i);
            tam_entrada = e.getNumBits();
            if(tam_entrada==1){
                /* CASO STD_LOGIC */
                if(e.getEsReloj()){
                    /* En el caso de que sea el reloj se mapea a la señal especial "senal_clk" */
                    escribirLinea("\t"+e.getNombre()+" => dato_listo,");
                }else{
                    escribirLinea("\t"+e.getNombre()+" => entrada_circuito("+bit_usados+"),");
                    bit_usados++;
                }
            }else{
                /* CASO STD_LOGIC_VECTOR */
                escribirLinea("\t"+e.getNombre()+"(0 to "+(tam_entrada-1)+") => entrada_circuito("+bit_usados+" to "+(bit_usados+tam_entrada-1)+"),");
                bit_usados = bit_usados + tam_entrada;
            }

        }
        for(int i=0;i<entidad.getNumSalidas();i++){
            Salida s = entidad.getSalida(i);
            if(entidad.getNumSalidas()-1==i){
                //EN EL CASO DE QUE SEA LA ÚLTIMA SALIDA NO SE PONE LA "," AL FINAL
                escribirLinea("\t"+s.getNombre()+" => SENAL_"+s.getNombre());
            }else{
                escribirLinea("\t"+s.getNombre()+" => SENAL_"+s.getNombre()+",");
            }
        }
        escribirLinea("\t);");
        escribirLinea("\t---------------------------------------------------------------");
        escribirLinea("");
    }

    private void end() {
        escribirLinea("");
        escribirLinea("end Behavioral;");
        escribirLinea("");
    }

    /**
     * Crea el nuevo fichero VHDL a partir de la entidad que tiene la clase.
     */
    public void crearFichero() {
        comentariosCabecera();
        librerias();
        entidadGeneral();
        inicioArquitectura();
        senalesCircuito();
        componenteCircuito();
        beginArquitectura();
        asignacionSenalesCircuito();
        asignacionComponenteCircuito();
        end();
    }
}
